Imports System
Imports System.Text

''' <summary>
''' Methods to parse and validate data.
''' </summary>
Public Class DataUtil

#Region "Private constructor"

	Private Sub New()
		' Can't create instance!
	End Sub

#End Region

#Region "Data validation"

	''' <summary>
	''' Checks if the object passed is null or empty.
	''' </summary>
	''' <returns>True if has value, otherwise false.</returns>
	Public Shared Function HasValue(ByVal value As Object) As Boolean

		If Not value Is Nothing AndAlso Not TypeOf value Is DBNull AndAlso Not String.IsNullOrEmpty(value.ToString()) Then
			Return True
		Else
			Return False
		End If

	End Function

	''' <summary>
	''' Checks if the passed value is a valid integer.
	''' </summary>
	''' <returns>True if a valid Int32, otherwise false.</returns>
	Public Shared Function IsNumeric(ByVal value As String) As Boolean

		Return IsNumeric(value, 0)

	End Function

	''' <summary>
	''' Checks if the passed value is a valid integer, with a variable to bind (ByRef).
	''' </summary>
	''' <returns>True if a valid Int32, otherwise false.</returns>
	Public Shared Function IsNumeric(ByVal value As String, ByRef intReturn As Integer) As Boolean

		If String.IsNullOrEmpty(value) Then

			Return False

		Else

			Try
				intReturn = Convert.ToInt32(value)
				Return True
			Catch
				Return False
			End Try

		End If

	End Function

	''' <summary>
	''' Checks if the value passed is True or False.
	''' </summary>
	''' <returns>True if the value is 1, TRUE, YES, ON. False for 0, FALSE, NO, OFF. Otherwise throws an InvalidOperationException.</returns>
	Public Shared Function TrueOrFalse(ByVal value As Object) As Boolean

		If Not HasValue(value) Then

			Return False

		End If

		Select Case value.ToString().ToUpper()
			Case "1", "TRUE", "YES", "ON"
				Return True
			Case "0", "FALSE", "NO", "OFF"
				Return False
			Case Else
				Throw New InvalidOperationException(value.ToString())
		End Select

	End Function

#End Region

#Region "Data parsing and conversion"

	''' <summary>
	''' Gets the passed value as seconds (for example 1m 10s should be 70).
	''' </summary>
	''' <returns>The value in seconds, as Int32.</returns>
	Public Shared Function ParseDurationToSeconds(ByVal value As String) As Integer

		Dim intReturn As Integer = 0

		Try

			If value.Contains("s") Then
				intReturn += ParseDurationValueFinder(value, "s")
			End If

			If value.Contains("m") Then
				intReturn += ParseDurationValueFinder(value, "m") * 60
			End If

			If value.Contains("h") Then
				intReturn += ParseDurationValueFinder(value, "h") * 60 * 60
			End If

			If value.Contains("d") Then
				intReturn += ParseDurationValueFinder(value, "d") * 60 * 60 * 24
			End If

			If intReturn = 0 Then
				intReturn = Convert.ToInt32(value)
			End If

		Catch
		End Try

		Return intReturn

	End Function

	''' <summary>
	''' Returns time in the format 00:00, where : is replaced by the specified separator.
	''' </summary>
	''' <returns>The time value, as String.</returns>
	Public Shared Function TimespanToTime(ByVal value As TimeSpan, ByVal separator As String) As String

		Return value.Hours.ToString("00") & separator & value.Minutes.ToString("00")

	End Function

	''' <summary>
	''' Returns the days difference between two dates.
	''' </summary>
	''' <returns>Integer (days).</returns>
	Public Shared Function DateDiff(ByVal date1 As DateTime, ByVal date2 As DateTime) As Integer

		Return DateDiff(date1, date2, "days")

	End Function

	''' <summary>
	''' Returns the specific datepart difference between two dates (default datepart is days)
	''' </summary>
	''' <returns>Integer (years, months, days, minutes or seconds).</returns>
	Public Shared Function DateDiff(ByVal date1 As DateTime, ByVal date2 As DateTime, ByVal datePart As String) As Integer

		Dim ts As TimeSpan = date1 - date2

		Select Case datePart
			Case "year", "yyyy", "yy", "y"
				Return ts.Days \ 365
			Case "month", "mon", "mo"
				Return ts.Days \ 30
			Case "day", "dd", "d"
				Return ts.Days
			Case "hour", "hh", "h"
				Return ts.Hours
			Case "minute", "min", "m"
				Return ts.Minutes
			Case "second", "sec", "s"
				Return ts.Seconds
		End Select

		Return ts.Days

	End Function

	''' <summary>
	''' Compare 2 strings and return the similarity from 0 to 100%, using the Levenshtein algorithm
	''' </summary>
	''' <returns>Integer (similarity from 0 to 100).</returns>
	Public Shared Function StringSimilarity(a As String, b As String) As Integer

		If String.IsNullOrEmpty(b) Or String.IsNullOrEmpty(b) Then
			Return 0
		End If

		If a = b Then
			Return 100
		End If

		Dim cost As Int32
		Dim d As Int32(,) = New Integer(a.Length, b.Length) {}
		Dim min1 As Int32
		Dim min2 As Int32
		Dim min3 As Int32

		For i As Int32 = 0 To d.GetUpperBound(0)
			d(i, 0) = i
		Next

		For i As Int32 = 0 To d.GetUpperBound(1)
			d(0, i) = i
		Next

		For i As Int32 = 1 To d.GetUpperBound(0)

			For j As Int32 = 1 To d.GetUpperBound(1)

				cost = Convert.ToInt32(Not (a(i - 1) = b(j - 1)))

				min1 = d(i - 1, j) + 1
				min2 = d(i, j - 1) + 1
				min3 = d(i - 1, j - 1) + cost
				d(i, j) = Math.Min(Math.Min(min1, min2), min3)

			Next

		Next

		Dim result As Integer = d(d.GetUpperBound(0), d.GetUpperBound(1))
		Dim length As Integer = a.Length

		If b.Length > length Then
			length = b.Length
		End If

		result = Convert.ToInt32(Math.Abs((1 - result / length) * 100))

		Return result

	End Function

	''' <summary>
	''' Returns the capitalized string (first letter uppercased).
	''' </summary>
	''' <returns>Capitalized string.</returns>
	Public Shared Function Capitalize(ByVal value As String) As String

		If Not String.IsNullOrEmpty(value) Then

			Return value.Substring(0, 1).ToUpper() & value.Substring(1, value.Length - 1)

		End If

		Return String.Empty

	End Function

#End Region

#Region "Data conversion"

	''' <summary>
	''' Converts the passed String to UT8ByteArray.
	''' </summary>
	''' <returns>Converted String as UT8ByteArray.</returns>
	Public Shared Function StringToUTF8ByteArray(ByVal value As String) As Byte()

		Return New UTF8Encoding().GetBytes(value)

	End Function

	''' <summary>
	''' Convert the passed UT8ByteArray to String.
	''' </summary>
	''' <returns>Converted UT8ByteArray as String.</returns>
	Public Shared Function UTF8ByteArrayToString(ByVal value() As Byte) As String

		Return New UTF8Encoding().GetString(value, 0, value.Length)

	End Function

#End Region

#Region "Get data as types"

	''' <summary>
	''' Return the passed object as String.
	''' </summary>
	''' <returns>Object converted to String.</returns>
	Public Shared Function GetString(ByVal value As Object) As String

		If value Is Nothing Then

			Return String.Empty

		Else

			Return value.ToString()

		End If

	End Function

	''' <summary>
	''' Return the passed object as Int32.
	''' </summary>
	''' <returns>Object converted to Int32.</returns>
	Public Shared Function GetInt32(ByVal value As Object) As Integer

		If value Is Nothing Then

			Return Int32.MinValue

		Else

			Return Convert.ToInt32(value)

		End If

	End Function

	''' <summary>
	''' Return the passed object as Double.
	''' </summary>
	''' <returns>Object converted to Double.</returns>
	Public Shared Function GetDouble(ByVal value As Object) As Double

		If value Is Nothing Then

			Return Decimal.MinValue

		Else

			Return Convert.ToDouble(value)

		End If

	End Function

	''' <summary>
	''' Return the passed object as Single.
	''' </summary>
	''' <returns>Object converted to Single.</returns>
	Public Shared Function GetSingle(ByVal value As Object) As Single

		If value Is Nothing Then

			Return Single.MinValue

		Else

			Return Convert.ToSingle(value)

		End If

	End Function

	''' <summary>
	''' Return the passed object as Boolean.
	''' </summary>
	''' <returns>Object converted to Boolean.</returns>
	Public Shared Function GetBoolean(ByVal value As Object) As Boolean

		If value Is Nothing Then

			Return False

		Else

			Dim r As String = value.ToString()

			If String.IsNullOrEmpty(r) Then

				Return False

			Else

				r = r.ToLower()

				Return (r = "1" OrElse r = "true" OrElse r = "yes")

			End If

		End If

	End Function

	''' <summary>
	''' Return the passed object as DateTime.
	''' </summary>
	''' <returns>Object converted to DateTime.</returns>
	Public Shared Function GetDateTime(ByVal value As Object) As DateTime

		If value Is Nothing Then

			Return DateTime.MinValue

		Else

			Return Convert.ToDateTime(value)

		End If

	End Function

#End Region

#Region "Internal methods"

	Private Shared Function ParseDurationValueFinder(ByVal value As String, ByVal identifier As String) As Integer

		Dim index As Integer
		Dim i As Integer

		Try

			value = value.ToLower()

			index = value.IndexOf(identifier)
			i = index - 1

			While i > -1 AndAlso IsNumeric(value.Substring(i, 1))
				i -= 1
			End While

			Return Convert.ToInt32(value.Substring(i, index))

		Catch ex As Exception

			Return 0

		End Try

	End Function

#End Region

End Class